#include "Ogre/ExampleApplication.h"
#include "CustomFrameListener.h"

//INCLUDE PRIME_SENSE CLASS
#include <XnVDeviceGenerator.h>
#include <XnVNite.h>
#include <XnTypes.h>
#include <XnV3DVector.h>

#include "SkeletonPoseDetector.h"

#include <assert.h>

#define VALIDATE_GENERATOR(type, desc, generator)					\
	{																\
	rc = m_Context.EnumerateExistingNodes(nodes, type);				\
		if (nodes.IsEmpty())										\
		{															\
			printf("No %s generator!\n", desc);						\
			return 1;												\
		}															\
	(*(nodes.Begin())).GetInstance(generator);						\
	}


//CLASS
class Player : public ExampleApplication
{
private:
	//CHARACTER MEMBER
	Ogre::SceneNode* _SinbadNode;
	Ogre::Entity* _SinbadEnt;

	//UPDATE MEMBER
	CustomFrameListener* FrameListener;
	
	//PRIME_SENSE MEMBERS
	Ogre::Vector3 m_origTorsoPos;

	xn::Context m_Context;
	xn::UserGenerator m_UserGenerator;
	xn::SceneAnalyzer m_SceneAnalyzer;

	XnVSessionManager* m_pSessionManager;
	XnVFlowRouter* m_pQuitFlow;
	XnVSelectableSlider1D* m_pQuitSSlider;

	XnUserID m_candidateID;

	XnCallbackHandle m_hPoseCallbacks;
	XnCallbackHandle m_hUserCallbacks;
	XnCallbackHandle m_hCalibrationCallbacks;

	StartPoseDetector * m_pStartPoseDetector;
	EndPoseDetector * m_pEndPoseDetector;


public:
	//CTOR
	Player();

	//INIT PRIME_SENSE
	XnStatus initPrimeSensor();

	//CREATE SCENE
	void createScene();
	void createCamera();

	//INIT UPDATE
	void createFrameListener();

	//INIT CHARACTER
	void setupBone(const String& name,const Ogre::Radian& angle, const Vector3 axis);
	void setupBone(const String& name,const Degree& yaw,const Degree& pitch,const Degree& roll);
	void setupBone(const String& name,const Ogre::Quaternion& q);
	void setupAnimations();

	//UPDATE USER ID
	void setUserID(XnUserID nUserId)
	{
		m_pEndPoseDetector->SetUserId(nUserId);
		FrameListener->setUserID(nUserId);
	}
	
	//CALLBACK OPEN-NI

	//NEW USER
	static void XN_CALLBACK_TYPE NewUser(xn::UserGenerator& generator, const XnUserID nUserId, void* pCookie)
	{
		// start looking for calibration pose for new users
		generator.GetPoseDetectionCap().StartPoseDetection("Psi", nUserId);
	}

	//LOST USER
	static  void XN_CALLBACK_TYPE LostUser(xn::UserGenerator& generator, const XnUserID nUserId, void* pCookie)
	{
		Player* This = (Player*)pCookie;
		if(This->m_candidateID == nUserId )
		{
			This->m_candidateID = 0;
			//This->resetBonesToInitialState();
			This->m_pEndPoseDetector->SetUserId(0);
			This->m_pStartPoseDetector->Reset();
		}
	}

	//CALIBRATION START
	static  void XN_CALLBACK_TYPE CalibrationStart(xn::SkeletonCapability& skeleton, const XnUserID nUserId, void* pCookie)
	{
	}

	//CALIBRATION END (START TRACKING)
	static  void XN_CALLBACK_TYPE CalibrationEnd(xn::SkeletonCapability& skeleton, const XnUserID nUserId, XnBool bSuccess, void* pCookie)
	{
		Player* This = (Player*)pCookie;

		if (bSuccess)
		{
			// start tracking
			skeleton.StartTracking(nUserId);
			This->m_pStartPoseDetector->SetStartPoseState(true);
			This->setUserID(nUserId);

			// save torso position
			XnSkeletonJointPosition torsoPos;
			skeleton.GetSkeletonJointPosition(nUserId, XN_SKEL_TORSO, torsoPos);
			This->m_origTorsoPos.x = -torsoPos.position.X;
			This->m_origTorsoPos.y = torsoPos.position.Y;
			This->m_origTorsoPos.z = -torsoPos.position.Z;
		}
		else
		{
			This->m_candidateID = 0;
		}
	}

	//DETECTION POSE
	static void XN_CALLBACK_TYPE PoseDetected(xn::PoseDetectionCapability& poseDetection, const XnChar* strPose, XnUserID nId, void* pCookie)
	{
		Player* This = (Player*)pCookie;

		// If we dont have an active candidate
		if(This->m_candidateID == 0)
		{
			This->m_candidateID = nId;
			This->m_UserGenerator.GetSkeletonCap().RequestCalibration(nId, TRUE);
			This->m_pStartPoseDetector->SetStartPoseState(true);
		}
	}

	//LOST POSE
	static void XN_CALLBACK_TYPE PoseLost(xn::PoseDetectionCapability& poseDetection, const XnChar* strPose, XnUserID nId, void* pCookie)
	{
		Player* This = (Player*)pCookie;
		This->m_pStartPoseDetector->Reset();
	}




};